package com.syjy;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.Options;
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
import com.syjy.container.ProtocolDataContainer;
import com.syjy.tunnelinfo.DataPoint;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 点位数据计算器
 * 公式中使用   P_点位_P  来划分点位
 *
 * @author: xiuwei
 * @version:
 */
@Slf4j
public class PointDataCalculator  {

  private static final Pattern expr = Pattern.compile("(?<=P_).*?(?=_P)");

  static {
    AviatorEvaluator.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
  }


  /**
   * 数据类型标识  0 :遥信  ； 1：遥测
   */
  @Getter
  private int dataTypeFlag;

  /**
   * 计算表达式
   */
  Expression compiledExp;
  /**
   * 计算所需参数
   */
  private Map<String, Integer> calculatingParameters;
  /**
   * 计算所需公式
   */
  private String formula;


  @Getter
  private final  Integer id;

  private ProtocolDataContainer protocolDataContainer = ProtocolDataContainer.getInstance();

  public PointDataCalculator(DataPoint dataPoint) {
    this.id=dataPoint.getId();
    if(StringUtils.isNoneEmpty(dataPoint.getFormula())){
      this.formula=dataPoint.getFormula();
    }
    if(dataPoint.getDataType()==ModbusDataTypeEnum.A16){
      dataTypeFlag =0;
    }else{
      dataTypeFlag =1;
    }
  }


  /**
   * 计算
   * 对公式进行计算
   *
   * @return V 计算完成的数据
   * @throws DataExchangeException 数据交换异常
   */
  public Object calculate() throws DataExchangeException {
    Map<String, Object> values = new HashMap<>();
    for (Map.Entry<String, Integer> e : getCalculatingParameters().entrySet()) {
      values.put(e.getKey(), protocolDataContainer.getData(e.getValue()));
    }
    try {
      return getOrInitCompiledExp().execute(values);
    } catch (ArithmeticException e) {
      log.error("除数为0,计算公式为：{},请检查数据池内是否有该点位是否有数据", this.formula);
      throw new DataExchangeException(10020,"除数为0,计算公式为：{},请检查数据池内是否有该点位是否有数据");
    } catch (Exception e) {
      throw new DataExchangeException(10021, "计算器计算数据时发生异常,公式为：" + this.formula);
    }
  }

  /**
   * 对公式进行初始化  如果公式解析失败将会抛出异常
   *
   * @return Expression  解析完成的表达式
   * @throws DataExchangeException 表达式解析失败时的异常
   */
  private Expression getOrInitCompiledExp() throws DataExchangeException {
    if (this.compiledExp == null) {
      try {
        this.compiledExp = AviatorEvaluator.compile(formula);
      } catch (ExpressionSyntaxErrorException e) {
        log.error("公式解析失败，公式不可用", e);
        throw new DataExchangeException(50001, "公式解析失败，公式不可用");
      }
    }
    return this.compiledExp;
  }


  /**
   * 对计算所需的参数进行整理
   *
   * @return P_（点位）_P  替换成真实的数据   字段--数值
   */
  public Map<String, Integer> getCalculatingParameters() {
    if (this.calculatingParameters == null) {
      this.calculatingParameters = new HashMap<>();
      Matcher matcher = expr.matcher(this.formula);
      while (matcher.find()) {
        String s = matcher.group();
        this.calculatingParameters.put("P_" + s + "_P", Integer.parseInt(s));
      }
    }
    return this.calculatingParameters;
  }

}
